<?php

namespace app\libs\orm;

use wicked\tools\Inflector;

class Collection implements \ArrayAccess, \Iterator, \Countable
{

    protected $table;

    protected $data = [];

    protected $query = [
        'where' => [],
        'inject' => [],
        'orderBy' => '',
        'limit' => ''
    ];

    protected $vars = [];

    public function __construct($table)
    {
        $this->table = Inflector::tablename($table);
        $this->find();
    }

    public function where()
    {
        $args = (array)func_get_args();

        if(!empty($args))
        {
            if(is_array($args[0]))
            {
                foreach($args[0] as $field => $value)
                    $this->where('`' . $field . '` = ?', $value);
            }
            else
            {
                $where = (array)array_shift($args);
                $this->query['where'] = str_replace('{table}', $this->table, $where);
                $this->vars = array_merge($this->vars, $args);
            }
        }

        return $this;
    }

    public function orderBy($string)
    {
        $this->query['orderBy'] = $string;
        return $this;
    }

    public function limit($string)
    {
        $this->query['limit'] = $string;
        return $this;
    }

    public function find($filters = [])
    {
        // find one by id
        if(!is_array($filters))
            return $this->findOne($filters);

        // where clause
        foreach($filters as $field => $value)
            $this->where('`' . $field . '` = ?', $value);

        // reset data
        $this->data = [];

        // get model
        $class = $this->model();

        // execute
        $stm = SimpleORM::pdo()->prepare($this->sql());
        $stm->execute($this->vars);
        foreach($stm->fetchAll(\PDO::FETCH_ASSOC) as $key => $entity)
            $this->data[$key] = new $class($entity, $this->table);

        return $this;
    }

    public function findOne($filters = [])
    {
        // find id
        if(!is_array($filters))
            $filters = [SimpleORM::convention('primary') => $filters];

        // limit 1
        $this->query['limit'] = '1';

        // run
        $this->find($filters);
        return (count($this->data) > 0) ? $this->data[0] : null;
    }

    public function set($field, $value)
    {
        foreach($this->data as $key => $entity)
            $this->data[$key] = $entity->set([$field => $value]);

        return $this;
    }

    public function save()
    {
        foreach($this->data as $key => $entity)
            $this->data[$key] = $entity->save();

        return $this;
    }

    public function delete()
    {
        foreach($this->data as $entity)
            $entity->delete();

        $this->data = [];

        return $this;
    }

    /**
     * Get SQL
     */
    protected function sql()
    {
        // select
        $sql = 'SELECT *';

        // from
        $sql .= ' FROM `' . $this->table . '`';

        // where
        if(!empty($this->query['where']))
            $sql .= ' WHERE ' . implode(' AND ', $this->query['where']);

        // join
        if(!empty($this->query['join']))
            foreach($this->query['join'] as $table => $on)
                $sql .= ' JOIN `' . $table . '` ON ' . $on;

        // order by
        if(!empty($this->query['orderBy']))
            $sql .= ' ORBER BY ' . $this->query['orderBy'];

        // limit
        if(!empty($this->query['limit']))
            $sql .= ' LIMIT ' . $this->query['limit'];

        return $sql;
    }

    public function __toString()
    {
        // select
        $sql = 'SELECT *';

        // from
        $sql .= ' FROM `' . $this->table . '`';

        // where
        if(!empty($this->query['where']))
        {
            $where = $this->query['where'];
            foreach($where as $key => $condition)
                $where[$key] = str_replace('?', $this->vars[$key], $condition);

            $sql .= ' WHERE ' . implode(' AND ', $where);
        }

        // join
        if(!empty($this->query['join']))
            foreach($this->query['join'] as $table => $on)
                $sql .= ' JOIN `' . $table . '` ON ' . $on;

        // order by
        if(!empty($this->query['orderBy']))
            $sql .= ' ORBER BY ' . $this->query['orderBy'];

        // limit
        if(!empty($this->query['limit']))
            $sql .= ' LIMIT ' . $this->query['limit'];

        return $sql;
    }

    protected function model()
    {
        $folder = SimpleORM::object('models');
        return empty($folder) ? SimpleORM::object('entity') : $folder . '\\' . Inflector::classname($this->table);
    }


    /*
     * Array Override
     */
    public function offsetGet($key)
    {
        return $this->data[$key];
    }

    public function offsetSet($key, $value)
    {
        if($value instanceof Record)
            $this->data[$key] = $value;
    }

    public function offsetExists($key)
    {
        return isset($this->data[$key]);
    }

    public function offsetUnset($key)
    {
        unset($this->data[$key]);
    }

    public function rewind()
    {
        reset($this->data);
    }

    public function current()
    {
        return current($this->data);
    }

    public function key()
    {
        return key($this->data);
    }

    public function next() {
        next($this->data);
    }

    public function valid() {
        return isset($this->data[$this->key()]);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->data);
    }

    public function count()
    {
        return count($this->data);
    }

}
